package deanandjames.ui;

import com.sun.j3d.utils.timer.J3DTimer;
import deanandjames.HorizontalPlatformGame;
import deanandjames.image.ImageLoader;
import deanandjames.image.PlayerImageWatcher;
import deanandjames.sound.SoundLoader;
import deanandjames.stats.StatisticsController;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

@SuppressWarnings("serial")
public class GamePanel extends JPanel implements Runnable, PlayerImageWatcher {
	private static final String[] explosionNames = {"explo1", "explo2", "explo3"};
	private static final String IMAGE_INFO_FILE = "imagesInfo.txt";
	private static final int MAX_FRAME_SKIPS = 5;
	private static final int NO_DELAYS_PER_YIELD = 16;
	private static final int PANEL_HEIGHT = 360;
	private static final int PANEL_WIDTH = 500;
	private static final String SOUNDS_FILE = "clipsInfo.txt";
	private Thread animator;
	private final SoundLoader soundLoader;
	private final Controller controller;
	private Graphics graphics;
	private Image graphicsImageBuffer = null;
	private final BufferedImage helpImage;
	private final long period;
	private final StatisticsController statisticsController;

	public GamePanel(HorizontalPlatformGame game, long period) {
		this.period = period;
		setDoubleBuffered(false);
		setBackground(Color.white);
		setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
		setFocusable(true);
		requestFocus();
		addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				controller.processKey(e);
			}
		});
		final ImageLoader imagesLoader = new ImageLoader(IMAGE_INFO_FILE);
		soundLoader = new SoundLoader(SOUNDS_FILE);
		helpImage = imagesLoader.getImage("title");

		controller = new Controller(PANEL_WIDTH, PANEL_HEIGHT, imagesLoader, this, period);

		Font messageFont = new Font("SansSerif", Font.BOLD, 24);
		statisticsController = new StatisticsController(getFontMetrics(messageFont), messageFont, PANEL_WIDTH, PANEL_HEIGHT);
	}

	@Override
	public void addNotify() {
		super.addNotify();
		startGame();
	}

	public void pauseGame() {
		controller.pause();
	}

	public void resumeGame() {
		if (!controller.isShowingHelp()) {
			controller.unpause();
		}
	}

	public void run() {
		long overSleepTime = 0L;
		int noDelays = 0;
		long excess = 0L;
		statisticsController.setGameStartTime(J3DTimer.getValue());
		long beforeTime = statisticsController.getGameStartTime();
		controller.startGame();
		while (controller.isGameRunning()) {
			controller.gameUpdate();
			gameRender();
			paintScreen();
			long afterTime = J3DTimer.getValue();
			long timeDiff = afterTime - beforeTime;
			long sleepTime = period - timeDiff - overSleepTime;
			if (sleepTime > 0) {
				try {
					Thread.sleep(sleepTime / 1000000L);
				} catch (final InterruptedException ex) {
				}
				overSleepTime = J3DTimer.getValue() - afterTime - sleepTime;
			} else {
				excess -= sleepTime;
				overSleepTime = 0L;
				if (++noDelays >= NO_DELAYS_PER_YIELD) {
					Thread.yield();
					noDelays = 0;
				}
			}
			beforeTime = J3DTimer.getValue();
			int skips = 0;
			while (excess > period && skips < MAX_FRAME_SKIPS) {
				excess -= period;
				controller.gameUpdate();
				skips++;
			}
		}
		System.exit(0);
	}

	public void sequenceEnded(String imageName) {
		controller.hideExplosion();
		controller.restartExplosionImage();
		if (statisticsController.exceededMaxNumberOfHits()) {
			controller.gameOver();
			statisticsController.setScore((int) ((J3DTimer.getValue() - statisticsController.getGameStartTime()) / 1000000000L));
			soundLoader.play("applause");
		}
	}

	public void showExplosion(int x, int y) {
		if (!controller.isShowingExplosion()) {
			controller.showExplosion();
			controller.setXExplosion(x - controller.getExplosionWidth() / 2);
			controller.setYExplosion(y - controller.getExplosionHeight() / 2);
			soundLoader.play(explosionNames[statisticsController.getNumHits() % explosionNames.length]);
			statisticsController.incrementNumHits();
		}
	}

	public void stopGame() {
		controller.stopGame();
	}

	private void gameRender() {
		if (graphicsImageBuffer == null) {
			graphicsImageBuffer = createImage(PANEL_WIDTH, PANEL_HEIGHT);
			if (graphicsImageBuffer == null) {
				System.out.println("dbImage is null");
				return;
			} else {
				graphics = graphicsImageBuffer.getGraphics();
			}
		}
		graphics.setColor(Color.white);
		graphics.fillRect(0, 0, PANEL_WIDTH, PANEL_HEIGHT);
		controller.display(graphics);
		if (controller.isShowingExplosion()) {
			controller.drawExplosion(graphics);
		}
		statisticsController.paint(graphics, controller.isGameOver());
		if (controller.isGameOver()) {
			statisticsController.paintGameOver(graphics);
		}
		if (controller.isShowingHelp()) {
			graphics.drawImage(helpImage, (PANEL_WIDTH - helpImage.getWidth()) / 2, (PANEL_HEIGHT - helpImage.getHeight()) / 2, null);
		}
	}

	private void paintScreen() {
		try {
			Graphics g = getGraphics();
			if (g != null && graphicsImageBuffer != null) {
				g.drawImage(graphicsImageBuffer, 0, 0, null);
			}
			Toolkit.getDefaultToolkit().sync();
			g.dispose();
		} catch (final Exception e) {
			System.out.println("Graphics context error: " + e);
		}
	}

	private void startGame() {
		if (animator == null || !controller.isGameRunning()) {
			animator = new Thread(this);
			animator.start();
		}
	}
}
